package com.witframework.security;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.Assert;

import com.witframework.util.CryptoUtility;




public class EncryptionUtil {

	private static final String DEFAULT_HASH_ALGORITHM = "SHA";

	public EncryptionUtil() {

	}

	public static String hash(String txt) throws NoSuchAlgorithmException {
		return hash(txt,DEFAULT_HASH_ALGORITHM);
	}

	public static String hash(String txt, String algorithm)
			throws NoSuchAlgorithmException {
		MessageDigest md = MessageDigest.getInstance(algorithm);
		byte encript[] = md.digest(txt.getBytes());
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < encript.length; i++) {
			result.append(Integer.toHexString(Math.abs(encript[i])));
			if (i < encript.length - 1) {
				result.append("-");
			}
		}
		return result.toString();
	}
	/**
     * Converts a String into a byte array using UTF-8, falling back to the
     * platform's default character set if UTF-8 fails.
     *
     * @param input the input (required)
     * @return a byte array representation of the input string
     */
    public static byte[] stringToByteArray(String input) {
        Assert.hasLength(input, "Input required");
        try {
            return input.getBytes("UTF-8");
        } catch (UnsupportedEncodingException fallbackToDefault) {
            return input.getBytes();
        }
    }

    /**
     * Converts a byte array into a String using UTF-8, falling back to the
     * platform's default character set if UTF-8 fails.
     *
     * @param byteArray the byte array to convert (required)
     * @return a string representation of the byte array
     */
    public static String byteArrayToString(byte[] byteArray) {
        Assert.notNull(byteArray, "ByteArray required");
        Assert.isTrue(byteArray.length > 0, "ByteArray cannot be empty");
        try {
            return new String(byteArray, "UTF8");
        } catch (final UnsupportedEncodingException e) {
            return new String(byteArray);
        }
    }

    private static byte[] cipher(String key, byte[] passedBytes, int cipherMode) throws EncryptionException {
        try {
            final KeySpec keySpec = new DESedeKeySpec(stringToByteArray(key));
            final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
            final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            final SecretKey secretKey = keyFactory.generateSecret(keySpec);
            cipher.init(cipherMode, secretKey);
            return cipher.doFinal(passedBytes);
        } catch (final Exception e) {
            throw new EncryptionException(e.getMessage(), e);
        }
    }
    private static Cipher cipher(byte[] key,int cipherMode) throws EncryptionException {
        try {
            final KeySpec keySpec = new DESedeKeySpec(key);
            final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
            final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
            final SecretKey secretKey = keyFactory.generateSecret(keySpec);
            cipher.init(cipherMode, secretKey);
            return cipher;
        } catch (final Exception e) {
            throw new EncryptionException(e.getMessage(), e);
        }
    }
    /**
     * Encrypts the inputString using the key.
     *
     * @param key at least 24 character long key (required)
     * @param inputString the string to encrypt (required)
     * @return the encrypted version of the inputString
     * @throws EncryptionException in the event of an encryption failure
     */
    public static String encrypt(String key, String inputString) throws EncryptionException {
        isValidKey(key);
        final byte[] cipherText = cipher(key, stringToByteArray(inputString), Cipher.ENCRYPT_MODE);
        return byteArrayToString(Base64.encodeBase64(cipherText));
    }

    /**
     * Encrypts the inputBytes using the key.
     *
     * @param key at least 24 character long key (required)
     * @param inputBytes the bytes to encrypt (required)
     * @return the encrypted version of the inputBytes
     * @throws EncryptionException in the event of an encryption failure
     */
    public static byte[] encrypt(String key, byte[] inputBytes) throws EncryptionException {
        isValidKey(key);
        return Base64.encodeBase64(cipher(key, inputBytes, Cipher.ENCRYPT_MODE));
    }

    /**
     * Decrypts the inputString using the key.
     *
     * @param key the key used to originally encrypt the string (required)
     * @param inputString the encrypted string (required)
     * @return the decrypted version of inputString
     * @throws EncryptionException in the event of an encryption failure
     */
    public static String decrypt(String key, String inputString) throws EncryptionException {
        Assert.hasText(key, "A key is required to attempt decryption");
        final byte[] cipherText = cipher(key, Base64.decodeBase64(stringToByteArray(inputString)), Cipher.DECRYPT_MODE);
        return byteArrayToString(cipherText);
    }

    /**
     * Decrypts the inputBytes using the key.
     *
     * @param key the key used to originally encrypt the string (required)
     * @param inputBytes the encrypted bytes (required)
     * @return the decrypted version of inputBytes
     * @throws EncryptionException in the event of an encryption failure
     */
    public static byte[] decrypt(String key, byte[] inputBytes) throws EncryptionException {
        Assert.hasText(key, "A key is required to attempt decryption");
        return cipher(key, Base64.decodeBase64(inputBytes), Cipher.DECRYPT_MODE);
    }

    private static void isValidKey(String key) {
        Assert.hasText(key, "A key to perform the encryption is required");
        Assert.isTrue(key.length() >= 24, "Key must be at least 24 characters long");
    }

	public static class EncryptionException extends RuntimeException {
        private static final long serialVersionUID = 1L;

        public EncryptionException(String message, Throwable t) {
            super(message, t);
        }

        public EncryptionException(String message) {
            super(message);
        }
    }
	
	
	
	public static void main(String[] args) throws Exception {		
		System.out.println(EncryptionUtil.hash("wuxinyang","MD5"));
	    System.out.println(EncryptionUtil.encrypt("4028c6001c0cf8e8011c0d12562a0018","wuxinyang"));
	    System.out.println(EncryptionUtil.decrypt("4028c6001c0cf8e8011c0d12562a0018","ty6j/bzjMKJ+Bh/l2+W2mg=="));
	    java.io.FileInputStream fis=new java.io.FileInputStream("E:\\file-backup\\myproject\\src\\config\\application.properties");
	    byte[] s=new byte[fis.available()];
	    fis.read(s);
	    System.out.println(new String(CryptoUtility.encrypt(s)));
	    System.out.println(new String(CryptoUtility.decrypt("123456789",CryptoUtility.encrypt("12345678",s))));	    
	}
	
}
